#pragma once
#include "Sock.hpp"
#include "Err.hpp"
#include "Log.hpp"
#include "Epoller.hpp"
#include "Ulit.hpp"
#include "Protocol.hpp"
#include <iostream>
#include <string>
#include <functional>
#include <unordered_map>
#include <cstring>
#include <cstdlib>
#include <cassert>
#include <sys/epoll.h>

using namespace protocol_ns;
class EpollServer;
const static int defaultport = 8888;

// connection对象，对相应的fd进行管理
class Connection
{
    const static int defaultfd = -1;
    using callback_t = std::function<void(Connection *)>;

public:
    Connection() : fd_(defaultfd)
    {
    }
    Connection(const int &fd, const std::string &clientip = "127.0.0.1", const uint16_t &clientport = defaultport)
        : fd_(fd), clientip_(clientip), clientport_(clientport)
    {
    }
    // 为对应的处理事件的函数注册方法
    void Register(callback_t readcb, callback_t writecb, callback_t exceptcb)
    {
        readcb_ = readcb;
        writecb_ = writecb;
        exceptcb_ = exceptcb;
    }

    ~Connection() {}

public:
    // IO信息
    int fd_;
    std::string inbuffer_;
    std::string outbuffer_;
    // IO方法，处理读、写和异常事件
    callback_t readcb_;
    callback_t writecb_;
    callback_t exceptcb_;
    // 客户端的IP地址和端口号，用于Debug
    std::string clientip_;
    uint16_t clientport_;
    // 管理需要让Epoll关心的事件
    uint32_t events_;
    // 回指指针，为了调用EpollServer中方法
    EpollServer *server_;
};

class EpollServer
{
    const static int gnum = 64;
    using func_t = std::function<Response(const Request &)>;

public:
    EpollServer(func_t func, int port = defaultport)
        : port_(defaultport),
          func_(func)
    {
    }

    // 添加fd，关心的事件
    bool AddConnection(int fd, uint32_t events, const std::string &clientip = "127.0.0.1", const uint16_t &clientport = defaultport)
    {
        // 1.因为是ET的工作模式，所以要将对应fd设置成非阻塞模式
        if (events & EPOLLET)
            Util::SetNonBlock(fd);
        // 2.将fd添加到connections_中进行管理
        // 2.1 new connection对象，填充相应的字段
        Connection *conn = new Connection(fd, clientip, clientport);
        // 监听套接字不关系写和异常事件
        if (fd == listensockfd_.Fd())
        {
            conn->Register(std::bind(&EpollServer::Accepter, this, std::placeholders::_1), nullptr, nullptr);
        }
        else
        { // 服务sockfd的事件处理注册
            conn->Register(std::bind(&EpollServer::Recv, this, std::placeholders::_1),
                           std::bind(&EpollServer::Send, this, std::placeholders::_1),
                           std::bind(&EpollServer::Exception, this, std::placeholders::_1));
        }
        conn->events_ = events;
        conn->server_ = this;
        // 2.1 将conn添加到connections中进行保存管理
        connections_.insert(std::make_pair(fd, conn));
        // 3.将fd添加到Epoll中进行监视
        epoll_.AddEvent(fd, events);

        // 日志打印
        logMessage(Info, "AddConnection successfully,fd:%d ,clientIP:%s ,clinetport:%d", fd, clientip.c_str(), clientport);
        return true;
    }

    void InitServer()
    {
        listensockfd_.Socket();
        listensockfd_.Bind(port_);
        listensockfd_.Listen();
        epoll_.Create();
        // 将listensock套接子调用AddConnection，加入管理
        AddConnection(listensockfd_.Fd(), EPOLLIN | EPOLLET);
    }
    // 分配器
    void Dispatcher()
    {
        while (true)
        {
            Looponce();
        }
    }
    void Looponce()
    {
        int n = epoll_.Wait(envs_, gnum, -1);
        // 对等待情况的进行判断
        if (n > 0)
        {
            // 有n个事件就绪
            for (int i = 0; i < n; ++i)
            {
                int fd = envs_[i].data.fd;
                uint32_t events = envs_[i].events;
                logMessage(Debug, "当前正在处理%d上的%s", fd, (events & EPOLLIN) ? "EPOLLIN" : "OTHER");
                // 我们将所有的异常情况，最后全部转化成为recv，send的异常！
                if ((events & EPOLLERR) || (events & EPOLLHUP))
                {
                    events |= (EPOLLIN | EPOLLOUT);
                }

                // 读事件就带用读的回调方法
                if ((events & EPOLLIN) && IsInConnections(fd))
                {
                    connections_[fd]->readcb_(connections_[fd]);
                }
                // 写事件就带用写的回调方法
                if ((events & EPOLLOUT) && IsInConnections(fd))
                {
                    connections_[fd]->writecb_(connections_[fd]);
                }
            }
        }
        else if (n == 0)
        {
            // 等待时间到了
            logMessage(Info, "Wait timeout...");
        }
        else
        {
            // 等待出错
            logMessage(Fatal, "Epoll wait error");
        }
    }
    // 监听套接字的读事件
    void Accepter(Connection *conn)
    {
        do
        {
            // 1.接受客户的sockfd
            std::string clientip;
            uint16_t clientport;
            int sockfd = listensockfd_.Accept(&clientip, &clientport);
            if (sockfd < 0)
                break;
            logMessage(Debug, "%s:%d 已经连上了服务器了", clientip.c_str(), clientport);
            // 2. 将用户添加到connections_中
            bool flag = AddConnection(sockfd, EPOLLIN | EPOLLET, clientip, clientport);

            if (!flag) // 存储失败，无法继续为客户进行服务
            {
                close(sockfd);
                logMessage(Warning, "接受客户失败：clientip:%s ,clientport:%d, sockfd:%d", clientip.c_str(), clientport, sockfd);
                break;
            }
            else // 存储成功
            {
                logMessage(Info, "接受客户成功：clientip:%s ,clientport:%d, sockfd:%d", clientip.c_str(), clientport, sockfd);
            }
        } while (conn->events_ & EPOLLET); // 循环读取，就是为了在ET工作模式下一下子读取完所有的数据
        logMessage(Debug, "accepter done ...");
    }

    // 处理读取用户的数据的事件
    bool RecvHelper(Connection *conn)
    {
        int sockfd;

        // 在ET的工作模式下，我们需要轮询的非阻塞读取数据（已经设置），直到读上来的数据小于预期的值
        do
        {
            // 1. 提取相关信息
            sockfd = conn->fd_;
            // 2. 读取数据
            char buffer[1024] = {0};
            int n = recv(sockfd, buffer, sizeof(buffer) - 1, 0);
            if (n > 0)
            {
                logMessage(Info, "recv data:%s[%d]", buffer, n);
                // 3. 处理数据
                // 3.1 将读上来的数据保存到inbuffer中
                conn->inbuffer_ += buffer;
            }
            else if (n == 0)
            {
                logMessage(Info, "client close");
                // 处理异常
                conn->exceptcb_(conn);
                break;
            }
            else
            {
                // 1. 因为以非阻塞的形式进行读取，会出现读事件没有就绪而报错
                if (errno == EAGAIN || errno == EWOULDBLOCK)
                {
                    logMessage(Info, "no data");
                }
                else // 2.真的出现了异常，进行异常处理
                {
                    logMessage(Warning, "recv error: %s", strerror(errno));
                    // 处理异常
                    conn->exceptcb_(conn);
                }
                break;
            }
        } while (conn->events_ & EPOLLET); // 循环读取，就是为了在ET工作模式下一下子读取完所有的数据
        return IsInConnections(sockfd);
    }

    void RequestHandler(Connection *conn)
    {
        int sockfd = conn->fd_;
        //  3.2 判断读上来的数据是否是一个完整的报文
        std::string package;
        int len;
        while (!(len = ReadStrPackage(sockfd, conn->inbuffer_, &package)))
            ; // 循环读取，直到取完一个报文就结束
        // 3.3 处理报文
        // 3.3.1 去掉报头
        package = RemoveHeads(package, len);
        // 3.3.2 反序列化
        Request req;
        req.Deserialization(package);
        // 3.3.3 调用业务处理函数
        Response resp = func_(req);
        // 3.3.4 序列化
        std::string resp_package = resp.Serialization();
        // 3.3.5 添加报头
        resp_package = AddHeads(resp_package);
        // 3.3.6 将发送的报文添加到发送缓冲区中
        conn->outbuffer_ += resp_package;
    }

    void Recv(Connection *conn)
    {

        logMessage(Debug, "开始处理读事件, fd: %d...", conn->fd_);

        if (RecvHelper(conn))
        {
            RequestHandler(conn);
            if (!conn->outbuffer_.empty())
            {
                // 触发一次读事件
                conn->server_->Send(conn);
            }
        }
    }
    // 处理将数据发送给用户的写事件
    void Send(Connection *conn)
    {
        int fd = conn->fd_;
        // 关心写事件是需要按需开启的，因为和读事件相反，写是缓冲区没有满的话就会一直通知你
        logMessage(Debug, "开始处理写事件, fd: %d ...", fd);
        do
        {
            int n = send(conn->fd_, conn->outbuffer_.c_str(), conn->outbuffer_.size(), 0);
            if (n > 0)
            {
                logMessage(Debug, "send %d bytes", n);
                conn->outbuffer_.erase(0, n);
                if (conn->outbuffer_.empty())
                    break;
            }
            else // 发送失败，可能是缓冲区满了，也可能是发生了异常
            {
                if (errno == EAGAIN || errno == EWOULDBLOCK)
                {
                    logMessage(Info, "no data");
                    break;
                }
                else
                {
                    logMessage(Warning, "send error: %s", strerror(errno));
                    // 处理异常
                    conn->exceptcb_(conn);
                    break;
                }
            }
        } while (conn->events_ & EPOLLET);
        if (IsInConnections(fd))
        {
            if (conn->outbuffer_.empty())
                EnableReadWrite(conn, true, false);
            else
                EnableReadWrite(conn, true, true);
        }
    }
    // 处理读写时发生异常的事件
    void Exception(Connection *conn)
    {
        logMessage(Debug, "开始处理异常事件, %d...", conn->fd_);

        // 0.提取相关信息
        int fd = conn->fd_;
        // 1. 将出现异常的sockfd冲Epoll管理中移除
        epoll_.DelEvent(fd);
        // 2.关闭相应的sockfd文件描述符
        close(fd);
        // 3. 删除connections_中sockfd维持的管理
        connections_.erase(fd);
        // 4. 删除conn，防止内存泄漏
        delete conn;
        // 5. 打印调试信息
        logMessage(Debug, "处理异常事件完成,sockfd:%d", fd);
    }

    void DebugPrint()
    {
    }

    // 控制是否关心读写事件
    bool EnableReadWrite(Connection *conn, bool read, bool write)
    {
        if (read)
        {
            conn->events_ |= EPOLLIN;
        }
        else
        {
            conn->events_ &= ~EPOLLIN;
        }
        if (write)
        {
            conn->events_ |= EPOLLOUT;
        }
        else
        {
            conn->events_ &= ~EPOLLOUT;
        }
        return epoll_.Mod(conn->fd_, conn->events_);
    }

    // 判断fd是否在connections_中的函数
    bool IsInConnections(int fd)
    {
        return connections_.find(fd) != connections_.end();
    }

    ~EpollServer()
    {
        listensockfd_.Close();
        epoll_.Close();
    }

private:
    uint16_t port_;
    Sock listensockfd_;
    struct epoll_event envs_[gnum];
    Epoll epoll_;
    func_t func_;
    // 用unordered_map conn_以fd为key值，映射fd的connection管理对象
    std::unordered_map<int, Connection *> connections_;
};
